/*
 * Copyright (C) 2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#include "exl.h"

#define zmsk		thr_zr

/*
 * Prototypes
 */
static eint32_t
void_pair(eint32_t rtype);

static eint32_t
int8_pair(eint32_t rtype);

static eint32_t
uint8_pair(eint32_t rtype);

static eint32_t
int16_pair(eint32_t rtype);

static eint32_t
uint16_pair(eint32_t rtype);

static eint32_t
int32_pair(eint32_t rtype);

static eint32_t
uint32_pair(eint32_t rtype);

static eint32_t
int_pair(eint32_t rtype);

static eint32_t
uint_pair(eint32_t rtype);

static eint32_t
mpz_pair(eint32_t rtype);

static eint32_t
mpq_pair(eint32_t rtype);

static eint32_t
cqq_pair(eint32_t rtype);

static eint32_t
float32_pair(eint32_t rtype);

static eint32_t
float_pair(eint32_t rtype);

static eint32_t
mpr_pair(eint32_t rtype);

static eint32_t
cdd_pair(eint32_t rtype);

static eint32_t
mpc_pair(eint32_t rtype);

static eint32_t
exact_pair(eint32_t rtype);

static eint32_t
inexact_pair(eint32_t rtype);

static eint32_t
dynamic_pair(eint32_t rtype);

static eint32_t
pair(eint32_t ltype, eint32_t rtype);

static eint32_t
arith(eint32_t ltype, eint32_t rtype);

static eint32_t
inc(eint32_t rtype);

static void
type_push(eint32_t type);

static void
type_pop(eint32_t count);

static void
types_init(elabel_t *label);

static void
types_next(elabel_t *base, elabel_t *label, eint32_t itype);

static void
types_again(elabel_t *label);

static ebool_t
types_merge(eint32_t *left, eint32_t *right, eint32_t length);

static void
infer(efunction_t *function, elabel_t *label);

/*
 * Initialization
 */
/* dfa gc root */
static evector_t	 *infer_root;

/* current set of types */
static eint32_t		 *infer_types;
static evector_t	 *infer_types_vector;

/* stack of types */
static eint32_t		 *infer_stack;
static evector_t	 *infer_stack_vector;

/*
 * Implementation
 */
void
init_infer(void)
{
    eobject_t	*root;

    evector(&infer_root, t_void, 2, 1);
    eadd_root((eobject_t *)&infer_root);
    root = infer_root->v.ptr;

    evector(&infer_types_vector, t_int32, 16, 1);
    root[0] = infer_types_vector;
    infer_types = infer_types_vector->v.i32;

    evector(&infer_stack_vector, t_int32, 16, 1);
    root[1] = infer_stack_vector;
    infer_stack = infer_stack_vector->v.i32;
}

void
finish_infer(void)
{
    erem_root((eobject_t *)&infer_root);
}

void
einfer(efunction_t *function)
{
    unsigned long	  bit;
    eint32_t		 *ivec;
    eint32_t		  bytes;
    elabel_t		 *label;
    elabel_t		**labels;
    eint32_t		  offset;
    esymbol_t		 *symbol;
    esymbol_t		**symbols;
    eint32_t		  num_labels;
    eint32_t		  num_symbols;
    eint32_t		  bytes_symbols;

    symbols = function->vsym->v.obj;
    num_symbols = function->vsym->offset;
    labels = function->labels->v.obj;
    num_labels = function->labels->offset;

    bytes_symbols = num_symbols * sizeof(eint32_t);
    for (offset = 0; offset < num_labels; offset++) {
	label = labels[offset];
	assert(label->ident == offset);
	bytes = label->stack * sizeof(eint32_t);
	if (bytes) {
	    if (label->infer_stack == null)
		evector(&label->infer_stack,
			t_int32, label->stack, 1);
	    else {
		if (label->infer_stack->length < label->stack)
		    erenew_vector(label->infer_stack, label->stack);
		memset(label->infer_stack->v.obj, 0, bytes);
	    }
	}
	if (label->infer_types == null)
	    evector(&label->infer_types,
		    t_int32, num_symbols, 1);
	else {
	    if (label->infer_types->length < num_symbols)
		erenew_vector(label->infer_types, num_symbols);
	    memset(label->infer_types->v.obj, 0, bytes_symbols);
	}
    }

    if (infer_types_vector->length < num_symbols) {
	erenew_vector(infer_types_vector, (num_symbols + 15) & ~15);
	infer_types = infer_types_vector->v.i32;
    }

    /* entry point */
    label = labels[0];
    ivec = label->infer_types->v.i32;
    for (offset = 0; offset < num_symbols; offset++) {
	symbol = symbols[offset];
	ivec[offset] = symbol->type ? symbol->type : t_dynamic;
    }

    mpz_set_ui(zmsk, 0);
    for (offset = 0; offset < num_labels; offset++) {
	label = labels[offset];
	memcpy(infer_types, label->infer_types->v.i32, bytes_symbols);
	infer(function, label);
	if (!mpz_tstbit(zmsk, offset) &&
	    memcmp(infer_types, label->infer_types->v.i32, bytes_symbols))
	    mpz_setbit(zmsk, offset);
    }
    while (mpz_sgn(zmsk)) {
	for (bit = mpz_scan1(zmsk, 0);
	     bit != ULONG_MAX; bit = mpz_scan1(zmsk, bit)) {
	    mpz_clrbit(zmsk, bit);
	    label = labels[bit];
	    memcpy(infer_types, label->infer_types->v.i32, bytes_symbols);
	    infer(function, label);
	    if (!mpz_tstbit(zmsk, bit) &&
		memcmp(infer_types, label->infer_types->v.i32, bytes_symbols))
		mpz_setbit(zmsk, bit);
	    ++bit;
	}
    }
}

static void
type_push(eint32_t type)
{
    if (infer_stack_vector->offset >= infer_stack_vector->length) {
	erenew_vector(infer_stack_vector, infer_stack_vector->length + 16);
	infer_stack = infer_stack_vector->v.i32;
    }
    infer_stack[infer_stack_vector->offset++] = type;
}

static void
type_pop(eint32_t count)
{
    infer_stack_vector->offset -= count;
    assert(infer_stack_vector->offset >= 0);
}

static void
types_init(elabel_t *label)
{
    if (label->stack) {
	if (infer_stack_vector->length < label->stack) {
	    erenew_vector(infer_stack_vector, infer_stack_vector->length + 16);
	    infer_stack = infer_stack_vector->v.i32;
	}
	memcpy(infer_stack, label->infer_stack->v.i32,
	       label->stack * sizeof(eint32_t));
	infer_stack_vector->offset = label->stack;
    }
    else
	infer_stack_vector->offset = 0;
}

static void
types_next(elabel_t *base, elabel_t *label, eint32_t itype)
{
    east_list_t		*list;
    ebool_t		 again;
    eint32_t		 num_symbols;

    num_symbols = fn_current->vsym->offset;
    again = false;
    list = elabel_list(label);
    if (ecfg_implicit_used_p(east_next(list))) {
	itype = pair(list->itype, itype);
	if (itype != list->itype) {
	    list->itype = itype;
	    again = true;
	}
    }
    if (types_merge(label->infer_types->v.i32,
		    base->infer_types->v.i32, num_symbols))
	again = true;
    if (label->stack &&
	types_merge(label->infer_stack->v.i32, infer_stack, label->stack))
	again = true;
    if (again)
	types_again(label);
}

static void
types_again(elabel_t *label)
{
    eobject_t		*dsts;
    eint32_t		 offset;

    if (!mpz_tstbit(zmsk, label->ident)) {
	mpz_setbit(zmsk, label->ident);
	for (dsts = label->dsts->v.obj, offset = 0;
	     offset < label->dsts->offset; offset++)
	    types_again(dsts[offset]);
    }
}

static ebool_t
types_merge(eint32_t *left, eint32_t *right, eint32_t length)
{
    eint32_t		itype;
    ebool_t		change;
    eint32_t		offset;

    for (change = false, offset = 0; offset < length; offset++) {
	itype = pair(left[offset], right[offset]);
	if (left[offset] != itype) {
	    left[offset] = itype;
	    change = true;
	}
    }

    return (change);
}

static eint32_t
void_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_void:					return (t_void);
	case t_int8:		case t_uint8:
	case t_int16:		case t_uint16:
	case t_int32:		case t_uint32:
	case t_int:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
int8_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_int8:					return (t_int8);
	case t_uint8:		case t_int16:		return (t_int16);
	case t_uint16:		case t_int32:		return (t_int32);
	case t_uint32:		case t_int:		return (t_int);
	case t_void:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
uint8_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_uint8:					return (t_uint8);
	case t_int8:		case t_int16:		return (t_int16);
	case t_uint16:		case t_int32:		return (t_int32);
	case t_uint32:		case t_int:		return (t_int);
	case t_uint:					return (t_uint);
	case t_void:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:					return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
int16_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_int8:		case t_uint8:
	case t_int16:					return (t_int16);
	case t_uint16:		case t_int32:		return (t_int32);
	case t_uint32:		case t_int:		return (t_int);
	case t_void:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
uint16_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_uint8:		case t_uint16:		return (t_uint16);
	case t_int8:		case t_int16:
	case t_int32:					return (t_int32);
	case t_uint32:		case t_int:		return (t_int);
	case t_uint:					return (t_uint);
	case t_void:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:					return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
int32_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_int8:		case t_uint8:
	case t_int16:		case t_uint16:
	case t_int32:					return (t_int32);
	case t_uint32:		case t_int:		return (t_int);
	case t_void:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
uint32_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_uint8:		case t_uint16:
	case t_uint32:					return (t_uint32);
	case t_int8:		case t_int16:
	case t_int32:		case t_int:		return (t_int);
	case t_uint:					return (t_uint);
	case t_void:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:					return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
int_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_int8:		case t_uint8:
	case t_int16:		case t_uint16:
	case t_int32:		case t_uint32:
	case t_int:					return (t_int);
	case t_void:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
uint_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_uint8:		case t_uint16:
	case t_uint32:		case t_uint:		return (t_uint);
	case t_void:		case t_int8:
	case t_int16:		case t_int32:
	case t_int:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:					return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
mpz_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_mpz:					return (t_mpz);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpq:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
mpq_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_mpq:					return (t_mpq);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_cqq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
cqq_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_cqq:					return (t_cqq);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_exact:		return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
float32_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_float32:					return (t_float32);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float:		case t_mpr:
	case t_cdd:		case t_mpc:
	case t_inexact:					return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
float_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_float:					return (t_float);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float32:		case t_mpr:
	case t_cdd:		case t_mpc:
	case t_inexact:					return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
mpr_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_mpr:					return (t_mpr);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float32:		case t_float:
	case t_cdd:		case t_mpc:
	case t_inexact:					return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
cdd_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_cdd:					return (t_cdd);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float32:		case t_float:
	case t_mpr:		case t_mpc:
	case t_inexact:					return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
mpc_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_mpc:					return (t_mpc);
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_inexact:					return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
exact_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:					return (t_exact);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_inexact:		case t_dynamic:		return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
inexact_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_dynamic:		return (t_dynamic);
	case t_float32:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:		return (t_inexact);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
dynamic_pair(eint32_t rtype)
{
    switch (rtype) {
	case t_void:		case t_int8:
	case t_uint8:		case t_int16:
	case t_uint16:		case t_int32:
	case t_uint32:		case t_int:
	case t_uint:		case t_mpz:
	case t_mpq:		case t_cqq:
	case t_exact:		case t_float32:
	case t_float:		case t_mpr:
	case t_cdd:		case t_mpc:
	case t_inexact:		case t_dynamic:		return (t_dynamic);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static eint32_t
pair(eint32_t ltype, eint32_t rtype)
{
    switch (ltype) {
	case t_void:			return (void_pair(rtype));
	case t_int8:			return (int8_pair(rtype));
	case t_uint8:			return (uint8_pair(rtype));
	case t_int16:			return (int16_pair(rtype));
	case t_uint16:			return (uint16_pair(rtype));
	case t_int32:			return (int32_pair(rtype));
	case t_uint32:			return (uint32_pair(rtype));
	case t_int:			return (int_pair(rtype));
	case t_uint:			return (uint_pair(rtype));
	case t_mpz:			return (mpz_pair(rtype));
	case t_mpq:			return (mpq_pair(rtype));
	case t_cqq:			return (cqq_pair(rtype));
	case t_float32:			return (float32_pair(rtype));
	case t_float:			return (float_pair(rtype));
	case t_mpr:			return (mpr_pair(rtype));
	case t_cdd:			return (cdd_pair(rtype));
	case t_mpc:			return (mpc_pair(rtype));
	case t_exact:			return (exact_pair(rtype));
	case t_inexact:			return (inexact_pair(rtype));
	case t_dynamic:			return (dynamic_pair(rtype));
	default:
	    if ((ltype|rtype) & t_vector)
		return (t_vector);
	    return (t_dynamic);
    }
}

/* FIXME there are some hacks here to not generate t_vector when loading
 * a vector element, because there is some confusing behavior with handling
 * of lvref.
 * The "#if 0" here should be kept for now to remember that without it,
 * wrong behavior will happen (JIT_DEBUG set in eemit_code.c will
 * trigger it for check/math.e:19514 at least...)
 */
static eint32_t
arith(eint32_t ltype, eint32_t rtype)
{
    switch (ltype) {
	case t_void:
	    switch (rtype) {
		case t_int8:		case t_uint8:
		case t_int16:		case t_uint16:
		case t_int32:		case t_uint32:
		case t_int:		case t_uint:
		case t_mpr:		case t_exact:
		case t_dynamic:				return (rtype);
		case t_void:		case t_mpz:
		case t_mpq:		case t_cqq:	return (t_exact);
		case t_float32:		case t_float:	return (t_float);
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_int8:		case t_uint8:
	    switch (rtype) {
		case t_int8:		case t_uint8:	return (t_int16);
		case t_int16:		case t_uint16:	return (t_int32);
		case t_int32:		case t_uint32:	return (t_int);
		case t_void:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:				return (t_exact);
		case t_float32:		case t_float:	return (t_float);
		case t_mpr:				return (t_mpr);
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_int16:		case t_uint16:
	    switch (rtype) {
		case t_int8:		case t_uint8:
		case t_int16:		case t_uint16:	return (t_int32);
		case t_int32:		case t_uint32:	return (t_int);
		case t_void:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:				return (t_exact);
		case t_float32:		case t_float:	return (t_float);
		case t_mpr:				return (t_mpr);
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_int32:		case t_uint32:
	    switch (rtype) {
		case t_int8:		case t_uint8:
		case t_int16:		case t_uint16:
		case t_int32:		case t_uint32:	return (t_int);
		case t_void:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:				return (t_exact);
		case t_float32:		case t_float:	return (t_float);
		case t_mpr:				return (t_mpr);
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_int:		case t_uint:
	case t_mpz:		case t_mpq:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:				return (t_exact);
		case t_float32:		case t_float:	return (t_float);
		case t_mpr:				return (t_mpr);
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_cqq:		case t_exact:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:				return (t_exact);
		case t_float32:		case t_float:
		case t_mpr:		case t_cdd:
		case t_mpc:		case t_inexact:	return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_float32:		case t_float:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_float32:
		case t_float:				return (t_float);
		case t_mpr:				return (t_mpr);
		case t_cqq:		case t_exact:
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_mpr:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_float32:
		case t_float:		case t_mpr:	return (t_mpr);
		case t_cqq:		case t_exact:
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_cdd:		case t_mpc:
	case t_inexact:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:		case t_float32:
		case t_float:		case t_mpr:
		case t_cdd:		case t_mpc:
		case t_inexact:				return (t_inexact);
		case t_dynamic:				return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	case t_dynamic:
	    switch (rtype) {
		case t_void:		case t_int8:
		case t_uint8:		case t_int16:
		case t_uint16:		case t_int32:
		case t_uint32:		case t_int:
		case t_uint:		case t_mpz:
		case t_mpq:		case t_cqq:
		case t_exact:		case t_float32:
		case t_float:		case t_mpr:
		case t_cdd:		case t_mpc:
		case t_inexact:		case t_dynamic:	return (t_dynamic);
		default:
#if 0
		    if (rtype & t_vector)		return (t_vector);
#endif
		    return (t_dynamic);
	    }
	default:
#if 0
	    if ((ltype|rtype) & t_vector)		return (t_vector);
#endif
	    return (t_dynamic);
    }
}

static eint32_t
inc(eint32_t rtype)
{
    switch (rtype) {
	case t_void:					return (t_int8);
	case t_int8:		case t_uint8:		return (t_int16);
	case t_int16:		case t_uint16:		return (t_int32);
	case t_int32:		case t_uint32:		return (t_int);
	case t_int:		case t_uint:
	case t_mpz:		case t_mpq:
	case t_cqq:					return (t_exact);
	case t_float32:					return (t_float);
	case t_exact:		case t_float:
	case t_mpr:		case t_cdd:
	case t_mpc:		case t_inexact:
	case t_dynamic:					return (rtype);
	default:
	    if (rtype & t_vector)			return (t_vector);
	    return (t_dynamic);
    }
}

static void
infer(efunction_t *function, elabel_t *label)
{
    edata_t		 data;
    east_list_t		*list;
    east_node_t		*node;
    east_node_t		*tnode;
    east_list_t		*tlist;
    eint32_t		 itype;
    eentry_t		*entry;
    ehash_t		*table;
    essa_store_t	*store;
    eint32_t		 offset;
    eobject_t		*stores;
    eint32_t		*itypes;

    itypes = label->infer_types->v.i32;
    stores = function->stores->v.ptr;
    types_init(label);

    list = label->list;
    itype = list->itype;
    node = east_node(list);
    for (list = east_next(list); list; list = east_next(list)) {
	node = east_node(list);
	list->rtype = itype;
	switch (node->code) {
	    case eop_label:
	    case eop_block:
		if (!label->ojmp)
		    types_next(label, node->clabel.label, itype);
		return;
	    case eop_nil:
		itype = t_void;
		break;
	    case eop_int:
		data.L = immediate_int(node);
		if (data.L >= -128 && data.L <= 127)
		    itype = t_int8;
		else if (data.L >= -32768 && data.L <= 32767)
		    itype = t_int16;
		else if (data.L >= -2147483648 && data.L <= 2147483647)
		    itype = t_int32;
		else
		    itype = t_int;
		break;
	    case eop_float:
		itype = t_float;
		break;
	    case eop_ll:
		itype = eobject_type(els[immediate_offset(node)]) & ~t_const;
		break;
	    case eop_ld:
	    case eop_lb:
		itype = itypes[node->csymbol.bit];
		if (node->csymbol.bit < function->stores->length &&
		    (store = stores[node->csymbol.bit])) {
		    offset = node->csymbol.ssa <= store->top ?
			node->csymbol.ssa : store->top;
		    for (; offset >= store->bot; offset--) {
			tlist = store->data[offset].list;
			if (tlist) {
			    itype = pair(itype, tlist->itype);
			    break;
			}
		    }
		}
		break;
	    case eop_ldt:
	    case eop_lbt:
		itype = node->csymbol.symbol->type;
		break;
	    case eop_ls:
		offset = infer_stack_vector->offset + node->cstack.offset;
		assert(offset >= 0 && offset < infer_stack_vector->offset);
		itype = infer_stack[offset];
		break;
	    case eop_lh:
		type_pop(1);
		itype = t_dynamic;
		break;
	    case eop_lhref:
		if (node->chash.arg)
		    type_push(t_hash);
		itype = t_hash;
		break;
	    case eop_lv:
		type_pop(node->cvector.rank);
		itype = infer_stack[infer_stack_vector->offset];
	    case eop_lvi:
		if (itype & t_vector)
		    itype &= ~t_vector;
		if (itype == t_void)
		    itype = t_dynamic;
		break;
	    case eop_lvref:
		type_pop(node->cvector.rank - 1);
		itype = infer_stack[infer_stack_vector->offset - 1];
		if (itype & t_vector)
		    itype &= ~t_vector;
		if (itype == t_void)
		    itype = t_dynamic;
		if (node->cvector.arg == 1) {
		    infer_stack[infer_stack_vector->offset] = itype;
		    type_push(t_vector);
		}
		else {
		    if (node->cvector.arg)
			type_push(t_vector);
		    infer_stack[infer_stack_vector->offset - 1] = itype;
		}
		break;
	    case eop_lrref:
		type_push(node->crecord.record->type);
		if (node->crecord.arg)
		    type_push(node->crecord.record->type);
	    case eop_lr:
		itype = node->crecord.symbol->type;
	    lr:
		if (itype == t_void || itype > elast_basic_type)
		    itype = t_dynamic;
		break;
	    case eop_lc:
		itype = node->csymbol.symbol->type;
		goto lr;
	    case eop_lref:
		itype = t_dynamic;
		break;
	    case eop_pdt:
	    case eop_pbt:
		itype = t_pointer|node->csymbol.symbol->type;
		break;
	    case eop_pv:
		type_pop(node->cvector.rank);
	    case eop_pd:
	    case eop_pb:
	    case eop_pvi:
	    case eop_pr:
	    case eop_pp:
		itype = t_pointer;
		break;
	    case eop_anon:
		if ((itype = node->crecord.record->type) == t_void)
		    itype = t_dynamic;
		break;
	    case eop_sd:
	    case eop_sb:
		switch (itype) {
		    case t_float32:
			itype = t_float;
			break;
		    case t_uint:
			itype = t_exact;
			break;
		    default:
			break;
		}
	    case eop_sc:
	    case eop_sdt:
	    case eop_sbt:
		itypes[node->csymbol.bit] =
		    pair(itypes[node->csymbol.bit], itype);
		break;
	    case eop_ss:
	    case eop_ssv:
		offset = infer_stack_vector->offset + node->cstack.offset;
		assert(offset >= 0 && offset < infer_stack_vector->offset);
		switch (itype) {
		    case t_float32:
			infer_stack[offset] = t_float;
			break;
		    case t_uint:
			infer_stack[offset] = t_exact;
			break;
		    default:
			infer_stack[offset] = itype;
			break;
		}
		break;
	    case eop_sst:
		offset = infer_stack_vector->offset + node->cstack.offset;
		assert(offset >= 0 && offset < infer_stack_vector->offset);
		infer_stack[offset] = node->cstack.type;
		break;
	    case eop_sh:
		type_pop(2);
		break;
	    case eop_shref:
		type_pop(!!node->chash.arg + 1);
		break;
	    case eop_sv:
		type_pop(node->cvector.rank + 1);
		break;
	    case eop_svi:
		type_pop(1);
		break;
	    case eop_svref:
		type_pop(!!node->cvector.arg + 1);
		break;
	    case eop_sr:
		type_pop(1);
		break;
	    case eop_srref:
		type_pop(!!node->crecord.arg + 1);
		break;
	    case eop_sref:
		type_pop(1);
		break;
	    case eop_jt:
	    case eop_jf:
	    case eop_j:
		types_next(label, node->clabel.label, itype);
		break;
	    case eop_jh:
		table = els[immediate_offset(node)];
		for (offset = 0; offset < table->size; offset++) {
		    for (entry = table->entries[offset];
			 entry; entry = entry->next) {
			tnode = entry->value;
			types_next(label, tnode->clabel.label, itype);
		    }
		}
		break;
	    case eop_enter:
		break;
	    case eop_begin:
		for (offset = node->cstack.length; offset; offset--)
		    type_push(t_dynamic);
		break;
	    case eop_off:
		type_push(t_int32);
		break;
	    case eop_push:
	    case eop_pushv:
		switch (itype) {
		    case t_float32:
			type_push(itype = t_float);
			break;
		    case t_uint:
			type_push(itype = t_exact);
			break;
		    default:
			type_push(itype);
			break;
		}
		break;
	    case eop_pusht:
		type_push(node->cstack.type);
		break;
	    case eop_pop:
		type_pop(1);
		itype = infer_stack[infer_stack_vector->offset];
		break;
	    case eop_call:
	    case eop_meth:
	    case eop_ecm:
	    case eop_blt:
	    case eop_apply:
		type_pop(node->capply.length);
		itype = pair(node->capply.type, t_dynamic);
		break;
	    case eop_ret:
	    case eop_reti:
		break;
	    case eop_rett:
	    case eop_retit:
		itype = node->creturn.type;
		break;
	    case eop_ne:
	    case eop_lt:
	    case eop_le:
	    case eop_eq:
	    case eop_ge:
	    case eop_gt:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = t_uint8;
		break;
	    case eop_and:
	    case eop_or:
	    case eop_xor:
	    case eop_shl:
	    case eop_shr:
	    case eop_trunc2:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
	    case eop_com:
	    case eop_floor:
	    case eop_trunc:
	    case eop_round:
	    case eop_ceil:
	    case eop_num:
	    case eop_den:
	    case eop_intp_v:
	    case eop_rational:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_exact:
			break;
		    default:
			itype = t_exact;
			break;
		}
		break;
	    case eop_mul2:
	    case eop_div2:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
		switch (itype) {
		    case t_int8:		case t_uint8:
			itype = t_int16;
			break;
		    case t_int16:		case t_uint16:
			itype = t_int32;
			break;
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
			itype = t_exact;
			break;
		    default:
			itype = pair(itype, itype);
			break;
		}
		break;
	    case eop_add:
	    case eop_sub:
	    case eop_mul:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = arith(list->ltype, list->rtype);
		break;
	    case eop_inc:
	    case eop_dec:
	    case eop_neg:
		itype = inc(list->rtype);
		break;
	    case eop_div:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
	    case eop_inv:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_mpq:
		    case t_exact:
			itype = t_exact;
			break;
		    default:
			itype = pair(itype, itype);
			break;
		}
		break;
	    case eop_rem:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
		itype = pair(itype, itype);
		break;
	    case eop_atan2:
	    case eop_pow:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
		itype = pair(list->ltype, t_inexact);
		break;
	    case eop_complex:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
		switch (itype) {
		    case t_void:
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_mpq:
		    case t_cqq:			case t_exact:
			itype = t_exact;
			break;
		    case t_float32:		case t_float:
		    case t_mpr:			case t_cdd:
		    case t_mpc:			case t_inexact:
			itype = t_inexact;
			break;
		    default:
			itype = t_dynamic;
			break;
		}
		break;
	    case eop_hypot:
		type_pop(1);
		list->ltype = infer_stack[infer_stack_vector->offset];
		itype = pair(list->ltype, list->rtype);
	    case eop_arg:
	    case eop_proj:
		switch (itype) {
		    case t_float32:		case t_float:
		    case t_cdd:
			itype = t_float;
			break;
		    default:
			itype = pair(itype, t_inexact);
			break;
		}
		break;
	    case eop_bool:
	    case eop_not:
	    case eop_integer_p:
	    case eop_rational_p:
	    case eop_float_p:
	    case eop_real_p:
	    case eop_complex_p:
	    case eop_number_p:
	    case eop_finite_p:
	    case eop_inf_p:
	    case eop_nan_p:
		itype = t_uint8;
		break;
	    case eop_signbit:
		itype = pair(itype, t_uint8);
		break;
	    case eop_signum:
		break;
	    case eop_real:
	    case eop_imag:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_mpq:
		    case t_exact:		case t_mpr:
		    case t_inexact:		case t_dynamic:
			break;
		    case t_cdd:
			itype = t_float;
			break;
		    case t_cqq:
			itype = t_exact;
			break;
		    case t_mpc:
			itype = t_mpr;
			break;
		    default:
			itype = pair(itype, t_inexact);
			break;
		}
		break;
	    case eop_conj:
		itype = pair(itype, itype);
		break;
	    case eop_abs:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_mpq:
		    case t_mpr:			case t_inexact:
		    case t_dynamic:
			break;
		    case t_cdd:
			itype = t_float;
			break;
		    case t_mpc:
			itype = t_mpr;
			break;
		    case t_cqq:			case t_exact:
			itype = t_inexact;
			break;
		    default:
			itype = pair(itype, t_inexact);
			break;
		}
		break;
	    case eop_sqrt:
	    case eop_asin:
	    case eop_acos:
	    case eop_atanh:
	    case eop_log:
	    case eop_log2:
	    case eop_log10:
		itype = pair(itype, t_inexact);
		break;
	    case eop_cbrt:
	    case eop_sin:
	    case eop_cos:
	    case eop_tan:
	    case eop_atan:
	    case eop_sinh:
	    case eop_cosh:
	    case eop_tanh:
	    case eop_asinh:
	    case eop_acosh:
	    case eop_exp:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_mpz:			case t_mpq:
		    case t_exact:		case t_mpr:
		    case t_inexact:
			itype = t_inexact;
			break;
		    case t_cdd:
			itype = t_float;
			break;
		    case t_mpc:
			itype = t_mpr;
			break;
		    default:
			itype = pair(itype, t_inexact);
			break;
		}
		break;
	    case eop_new:
		itype = node->crecord.record->type;
		break;
	    case eop_mv:
		type_pop(1);
		itype = infer_stack[infer_stack_vector->offset];
		break;
	    case eop_vnew:
		type_pop(node->cvector.rank - 1);
	    case eop_vnewi:
		itype = t_vector|node->cvector.type;
		break;
	    case eop_vmv:
	    case eop_renew:
		type_pop(1);
		itype = t_vector|infer_stack[infer_stack_vector->offset];
		break;
	    case eop_renewi:
		break;
	    case eop_fref:
		itype = t_lambda;
		break;
	    case eop_aret:
		break;
	    case eop_szofdim:
		type_pop(1);
	    case eop_szof:
	    case eop_szofva:
	    case eop_szofdimi:
	    case eop_tpof:
	    case eop_rankof:
		itype = t_int32;
		break;
	    case eop_subtpof:
		type_pop(1);
		itype = t_uint8;
		break;
	    case eop_vas:
		break;
	    case eop_val:
		type_pop(node->cstack.length);
		break;
	    case eop_lva:
	    case eop_lvai:
		itype = t_dynamic;
		break;
	    case eop_sva:
		type_pop(1);
	    case eop_svai:
		break;
	    case eop_pva:
		itype = t_pointer;
		break;
	    case eop_try:
		type_push(t_dynamic);
		type_push(t_dynamic);
		type_push(t_dynamic);
		type_push(t_dynamic);
		itype = t_dynamic;
		break;
	    case eop_catch:
		assert(label->stack == infer_stack_vector->offset);
		type_pop(4);
	    case eop_throw:
		itype = t_dynamic;
		break;
	    case eop_unwind:
		if (node->cexcept.arg)
		    type_pop(4);
		break;
	    case eop_exit:
	    case eop_setup:
		break;
	    case eop_intp_0:
	    case eop_nump_0:
		itype = t_uint8;
		break;
	    case eop_intp_n1:
		itype = t_int8;
		break;
	    case eop_realp_v:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_float32:		case t_float:
		    case t_mpz:			case t_mpq:
		    case t_exact:		case t_mpr:
		    case t_inexact:
			break;
		    default:
			itype = t_dynamic;
			break;
		}
		break;
	    case eop_nump_v:
		switch (itype) {
		    case t_int8:		case t_uint8:
		    case t_int16:		case t_uint16:
		    case t_int32:		case t_uint32:
		    case t_int:			case t_uint:
		    case t_float32:		case t_float:
		    case t_mpz:			case t_mpq:
		    case t_exact:		case t_mpr:
		    case t_cdd:			case t_cqq:
		    case t_mpc:
			break;
		    default:
			itype = t_dynamic;
			break;
		}
		break;
	    case eop_note:
		break;
	    default:
		abort();
	}
	list->itype = itype;
    }
}
